package server;

import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.Node;
import org.dom4j.io.SAXReader;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.*;

/**
 * Minicat的主类
 */
public class Bootstrap {

    /**定义socket监听的端口号*/
    private int port = 8080;

    public int getPort() {
        return port;
    }

    public void setPort(int port) {
        this.port = port;
    }

    private Mapper mapper;


    /**
     * Minicat启动需要初始化展开的一些操作
     */
    public void start() throws Exception {

        //加载server.xml
        mapper = new Mapper();
        loadServer(mapper);





        // 定义一个线程池
        int corePoolSize = 10;
        int maximumPoolSize =50;
        long keepAliveTime = 100L;
        TimeUnit unit = TimeUnit.SECONDS;
        BlockingQueue<Runnable> workQueue = new ArrayBlockingQueue<>(50);
        ThreadFactory threadFactory = Executors.defaultThreadFactory();
        RejectedExecutionHandler handler = new ThreadPoolExecutor.AbortPolicy();


        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
                corePoolSize,
                maximumPoolSize,
                keepAliveTime,
                unit,
                workQueue,
                threadFactory,
                handler
        );





        /*
            完成Minicat 1.0版本
            需求：浏览器请求http://localhost:8080,返回一个固定的字符串到页面"Hello Minicat!"
         */
        ServerSocket serverSocket = new ServerSocket(port);
        System.out.println("=====>>>Minicat start on port：" + port);




        System.out.println("=========>>>>>>使用线程池进行多线程改造");
        /*
            多线程改造（使用线程池）
         */
        while(true) {

            Socket socket = serverSocket.accept();
//            RequestProcessor requestProcessor = new RequestProcessor(socket,servletMap);
            //改造线程参数。将封装好的mapper传入
            RequestProcessor requestProcessor = new RequestProcessor(socket,mapper);
            //requestProcessor.start();
            threadPoolExecutor.execute(requestProcessor);
        }



    }
    // 假设这个minicat只有一个server
    private void loadServer(Mapper mapper) {

        InputStream resourceAsStream = this.getClass().getClassLoader().getResourceAsStream("server.xml");
        SAXReader saxReader = new SAXReader();
        try{
            Document document = saxReader.read(resourceAsStream);
            Element rootElement = document.getRootElement();

            //假设一个server里只有一个connector，所有用singleNode
            Element node = (Element)rootElement.selectSingleNode("//Connector");
            //拿到端口号
            String port = node.attributeValue("port");
            this.port = Integer.parseInt(port);

            //一个server只有一个host
            List<Element> list = rootElement.selectNodes("//Host");
            for (Element element : list) {
                String name = element.attributeValue("name");
                Host host = new Host();

                String appBase = element.attributeValue("appBase").replaceAll("\\.","/");
                doscan(appBase,appBase,host);
                mapper.getHostMap().put(name,host);

            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    //扫描server.xml host appBase指定的路径。并开始封装host
    private void doscan(String scanPath,String orginalPath,Host host){
        File pack = new File(scanPath);
        File[] files = pack.listFiles();
        for (File file : files) {

            if(file.isDirectory()){
                doscan(scanPath+"/"+file.getName(),orginalPath,host);
            }
            if(file.getName().equals("web.xml")){
                //web.xml 放在web应用的目录下。先获取web应用的文件夹名，当作contextMap的key；
                String contextName = file.getAbsolutePath().replace(orginalPath+"\\", "").replace("\\web.xml","");
                Context context = new Context();
                //解析web.xml。 封装context
                readWebXml(file,context);
                host.getContextMap().put(contextName,context);




            }
        }
    }

    /**
     *  解析web。xml  找servlet，与serlvet-mapping‘的对应关系，封装到wapperMap中。
     * @param file
     */
    private void readWebXml(File file,Context context){
        SAXReader saxReader = new SAXReader();
        try {
            Document document = saxReader.read(file);
            Element rootElement = document.getRootElement();
            //找serlvet
            List<Element> selectNodes = rootElement.elements("servlet");
            for (int i = 0; i < selectNodes.size(); i++) {
                Element element = selectNodes.get(i);
                // <servlet-name>lagou</servlet-name>
                Element servletnameElement =  element.element("servlet-name");
                String servletName = servletnameElement.getStringValue();
                // <servlet-class>server.LagouServlet</servlet-class>
                Element servletclassElement =  element.element("servlet-class");
                String servletClass = servletclassElement.getStringValue();

                String urlPattern = "";
                // 根据servlet-name的值找到url-pattern
                List<Element> elements = rootElement.elements("servlet-mapping");
                for (Element element1 : elements) {
                    if( element1.element("servlet-name").getStringValue().equals(servletName)){
                        urlPattern = element1.element("url-pattern").getStringValue();
                        break;
                    }
                }

                //找到serlvet.class文件。开始用自定义类加载器把class文件载入。并将其封装到wrapper中
                String servletPath = file.getAbsolutePath().replace("web.xml","")+servletClass.replace(".","\\")+".class";
                MyClassLoader myClassLoader = new MyClassLoader(servletPath);

                Class<?> aClass = myClassLoader.loadClass(servletClass.replace("/","."));


                HttpServlet servlet = (HttpServlet) aClass.newInstance();
                Wrapper wrapper = new Wrapper(servlet);
                context.getWrapperMap().put(urlPattern,wrapper);
            }
        }catch (Exception e){
            e.printStackTrace();
        }

    }



    /**
     * Minicat 的程序启动入口
     * @param args
     */
    public static void main(String[] args) {
        Bootstrap bootstrap = new Bootstrap();
        try {
            // 启动Minicat
            bootstrap.start();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
